/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */


#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>


#include <resolv.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "rules.h"
#include "cpipes.h"


/*************************************************************************
 * Setting up the pipes
 *************************************************************************/
IPCommunicationPipes::IPCommunicationPipes( int size, FilterChain*filterChain){
	int i;
	this->filterChain = filterChain;
	commpipes_total=size;

	pipes = (PIPES*)malloc(size*sizeof(PIPES));
	for(i = 0 ; i < commpipes_total ; i++){
		pipes[i].used = 0;
		pipes[i].pipes[0] = 0;
		pipes[i].pipes[1] = 0;
		pipes[i].thread_id = 0;
	}
	
	commpipes_maxfd=0;

}


/*************************************************************************
 * Destroying the pipes
 *************************************************************************/
IPCommunicationPipes::~IPCommunicationPipes(){
	int i;
	for( i = 0 ; i < commpipes_total ; i++ ){
		pipeFinished(&pipes[i]);
	}
	free(pipes);
}


/*************************************************************************
 * Preparing the pipes for read
 *************************************************************************/
void IPCommunicationPipes::PrepareForRead(fd_set* prFd, fd_set* peFd){
	int i;

	reapZombiePipes();

	for(i = 0 ; i < commpipes_total ; i++){
		if (pipes[i].used == 1 ){
			FD_SET(pipes[i].pipes[0], prFd);
			FD_SET(pipes[i].pipes[0], peFd);
			if (pipes[i].pipes[0] > commpipes_maxfd)
				commpipes_maxfd = pipes[i].pipes[0];
		}
		else{
			FD_CLR(pipes[i].pipes[0], prFd);
			FD_CLR(pipes[i].pipes[0], peFd);
		}
	}
}


/*************************************************************************
 * Getting a free pipe
 *************************************************************************/
PIPES* IPCommunicationPipes::GetPipe( fd_set* prFd, const ipaddress * ip){
	int i;
	for( i = 0 ; i < commpipes_total ; i++ ){
		if(pipes[i].used ==0){
			if(pipe(pipes[i].pipes)< 0){
				logit(LOG_CRITICAL)<<"Core - Couldn't allocate pipes "<<errno<<lend;
				return 0;
			}
			else{
				memcpy(&(pipes[i].ip), ip, sizeof(ipaddress));
				if(fcntl(pipes[i].pipes[0], F_SETFL, O_NONBLOCK)==-1 ||
					fcntl(pipes[i].pipes[1], F_SETFL, O_NONBLOCK)==-1){
					logit(LOG_CRITICAL)<<"Core - Pipe fcntl error"<<lend;
					close(pipes[i].pipes[0]);
					close(pipes[i].pipes[1]);
					return 0;
				}
				pipes[i].used = 1;
				break;
			}
		}	
	}
	
	return &pipes[i];
}


/*************************************************************************
 * Get the number of free pipes available
 *************************************************************************/
int IPCommunicationPipes::GetFreePipes(){
	int i, counter=0;
	for(i = 0 ; i < commpipes_total ; i++){
		if(pipes[i].used ==1)
			counter++;
	}
	return commpipes_total-counter;
}


/*************************************************************************
 * Read from the active pipes
 *************************************************************************/
int IPCommunicationPipes::ReadFromThePipes(fd_set* prFd, fd_set* peFd ){
	int i;
	int retv;
	char commreadchar[COMMREADCHAR_SIZE];

	for(i = 0 ; i <  commpipes_total ; i++ ){
		if(pipes[i].used == 1 ){
			if(FD_ISSET(pipes[i].pipes[0], prFd)){
				memset(commreadchar,0,COMMREADCHAR_SIZE);
				retv = read(pipes[i].pipes[0],commreadchar,COMMREADCHAR_SIZE);
				if(retv > 0){
					filterChain->PassFilters(FilterChain::GetMessageType(commreadchar), flattenip(pipes[i].ip), commreadchar);
				}
				else{
					//FDLCLR(pipes[i].pipes[0], prFd);
					//FDLCLR(pipes[i].pipes[0], peFd);
					pipeFinished(&pipes[i]);
				}
			}
			if(FD_ISSET(pipes[i].pipes[0], peFd)){
				pipeFinished(&pipes[i]);
			}
		}
	}
	return 0;
}

void IPCommunicationPipes::reapZombiePipes(){
	char x;
	int i;
	logd<<"pipe reaping"<<lend;
	for(i = 0 ; i <  commpipes_total ; i++ ){
		if(pipes[i].used ==1 ){
			if(read(pipes[i].pipes[0], &x, 0)<0){
				if(errno!=EINTR || errno != EAGAIN){
					logd<<"Recovering the lost pipes"<<lend;
					pipeFinished(&pipes[i]);
				}
			}
		}
	}
}

/*************************************************************************
 * Called when the pipe has closed
 *************************************************************************/
void IPCommunicationPipes::pipeFinished(PIPES* pipes){
	if(pipes->used){
		filterChain->PassFilters( FILTER_END, flattenip(pipes->ip), 0);
		close(pipes->pipes[0]);
		close(pipes->pipes[1]);
		pipes->used=0;

#ifdef THREADXX
		if(pipes->thread_id)
			pthread_join(pipes->thread_id, 0);
#endif
	}
}


/*************************************************************************
 * Return the max_fd for select
 *************************************************************************/
int IPCommunicationPipes::GetMaxFd(){
	return commpipes_maxfd;
}
